﻿using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Text;
using System.Threading;

namespace XSerialPort
{
    public class LV
    {
        public string Val { get; set; }

        public string Dis { get; set; }
    }


    public class PortHelper
    {

        public delegate void SendEventPortClick(string msg);
        public static event SendEventPortClick SendPort;
        private static SerialPort _sPort;

        public static bool IsShowMsg = false;

        public static SerialPort sPort
        {
            get
            {
                if (null == _sPort)
                {
                    _sPort = new SerialPort();
                }
                return _sPort;
            }
            set { _sPort = value; }
        }


        #region 枚举类型
        /// <summary>
        /// 停止位
        /// </summary>
        public enum StopBits
        {
            None = 0,
            One = 1,
            Two = 2,
            OnePointFive = 3
        }

        /// <summary>
        /// 校验位
        /// </summary>
        public enum Parity
        {
            None = 0,
            Odd = 1,
            Even = 2,
            Mark = 3,
            Space = 4
        }
        #endregion

        #region 结构体
        public struct ConfigComType
        {
            public static string PortName;
            public static int BaudRate;
            public static int DataBits;
            public static StopBits StopBits;
            public static Parity Parity;
        }
        #endregion

        /// <summary>
        /// 是否打开
        /// </summary>
        public static bool IsOpen
        {
            get { return sPort.IsOpen; }
        }
        /// <summary>
        /// 清空缓存
        /// </summary>
        public static void DiscardInBuffer()
        {
            if (sPort.IsOpen)
            {
                sPort.DiscardInBuffer();
                sPort.DiscardOutBuffer();
               
            }

        }

        #region 静态方法
        /// <summary>
        /// 获取串口列表
        /// </summary>
        public static string[] GetSerialProtNames
        {
            get
            {
                return SerialPort.GetPortNames();
            }
        }



        /// <summary>
        /// 判断端口号是否存在
        /// </summary>
        /// <param name="strPotrName">待检测的端口号</param>
        /// <returns></returns>
        public static bool IsCom(string strPotrName)
        {
            string[] arrPortNames = GetSerialProtNames;
            if (Array.IndexOf(arrPortNames, strPotrName) >= 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        #endregion
        /// <summary>
        /// 关闭串口
        /// </summary>
        /// <param name="strError">错误信息</param>
        public static void CloseCom(out string strError)
        {
            try
            {
                if (sPort.IsOpen)
                {
                    sPort.Close();
                    strError = "串口关闭成功！";
                }
                else
                {
                    strError = "串口未打开！";
                }

            }
            catch (Exception e)
            {
                strError = e.Message;
            }

        }

        /// <summary>
        /// 打开串口
        /// </summary>
        /// <param name="strError"></param>
        /// <returns></returns>
        public static bool OpenCom(out string strError)
        {
            //检测端口合法性
            if (IsCom(ConfigComType.PortName) == false)
            {
                strError = "端口号不存在，无法开启端口。";
                return false;
            }
            else
            {
                //开启端口
                try
                {
                    //写入串口配置
                    sPort.PortName = ConfigComType.PortName;
                    sPort.BaudRate = ConfigComType.BaudRate;
                    sPort.DataBits = ConfigComType.DataBits;
                    sPort.StopBits = (System.IO.Ports.StopBits)ConfigComType.StopBits;
                    sPort.Parity = (System.IO.Ports.Parity)ConfigComType.Parity;

                    //尝试关闭串口
                    CloseCom(out strError);

                    sPort.Open();

                    strError = string.Format("打开{0}串口", sPort.PortName);
                    return sPort.IsOpen;
                }
                catch (Exception e)
                {
                    strError = e.Message;
                    return false;
                }
            }
        }

        private static bool IsCom(object portName)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 串口
        /// </summary>
        /// <returns></returns>
        public static List<LV> GetPorts()
        {
            List<LV> list = new List<LV>();
            string[] ports = SerialPort.GetPortNames();
            foreach (var item in ports)
            {
                list.Add(new LV() { Val = item, Dis = item });
            }
            return list;
        }
        /// <summary>
        /// 波特率
        /// </summary>
        /// <returns></returns>
        public static List<LV> GetRate()
        {

            List<LV> list = new List<LV>();
            //list.Add(new LV() { Val = "4800", Dis = "4800" });
            list.Add(new LV() { Val = "9600", Dis = "9600" });
            list.Add(new LV() { Val = "19200", Dis = "19200" });
            list.Add(new LV() { Val = "38400", Dis = "38400" });
            list.Add(new LV() { Val = "57600", Dis = "57600" });
            list.Add(new LV() { Val = "115200", Dis = "115200" });
            list.Add(new LV() { Val = "128000", Dis = "128000" });
            return list;
        }
        /// <summary>
        /// 校验位
        /// </summary>
        /// <returns></returns>
        public static List<LV> GetParity()
        {

            List<LV> list = new List<LV>();
            list.Add(new LV() { Val = "0", Dis = "None" });
            list.Add(new LV() { Val = "1", Dis = "Odd" });
            list.Add(new LV() { Val = "2", Dis = "Even" });
            list.Add(new LV() { Val = "3", Dis = "Mark" });
            list.Add(new LV() { Val = "4", Dis = "Space" });

            return list;
        }
        /// <summary>
        /// 数据位
        /// </summary>
        /// <returns></returns>
        public static List<LV> GetDataBits()
        {
            List<LV> list = new List<LV>();
            list.Add(new LV() { Val = "8", Dis = "8" });
            return list;
        }
        /// <summary>
        /// 停止位
        /// </summary>
        /// <returns></returns>
        public static List<LV> GetStopBits()
        {
            List<LV> list = new List<LV>();
            list.Add(new LV() { Val = "0", Dis = "0" });  //None
            list.Add(new LV() { Val = "1", Dis = "1" });  //One
            list.Add(new LV() { Val = "2", Dis = "2" });  //Two
            list.Add(new LV() { Val = "3", Dis = "3" });  //OnePointFive
            return list;
        }


        public static byte ReadRegData(byte a, uint ADDR, ref uint DATA)
        {
            byte[] readbuf = new byte[15];
            byte[] buf = new byte[10];
            byte value = 0x00;
            int recv_cnt = 0x00;
            uint byte_sel = ADDR & 0x3;
            //byte_sel = (3 - byte_sel) * 8;

            //TXD0 ADDRESS
            readbuf[0] = a;
            //TXD0 ADDRESS
            readbuf[1] = (byte)((ADDR >> 0) & 0xff);
            readbuf[2] = (byte)((ADDR >> 8) & 0xff);
            readbuf[3] = (byte)((ADDR >> 16) & 0xff);
            readbuf[4] = (byte)((ADDR >> 24) & 0xff);
            if (IsOpen)
            {
                //DiscardInBuffer();
                sPort.Write(readbuf, 0, 5);
                SengMsg($" Send Read Cmd: {bufMsg(readbuf, 5)}");
                int i;
                for (i = 0; i < 100; i++)
                {
                    if (sPort.BytesToRead > 0)
                    {
                        int n = sPort.BytesToRead;//先记录下来，避免某种原因，人为的原因，操作几次之间时间长，缓存不一致     
                        //声明一个临时数组存储当前来的串口数据   

                        if (n >= 8)
                            n = 8;
                        sPort.Read(buf, 0, n);//读取缓冲数据

                        if ((recv_cnt + n) >= 12)
                        {
                            n = 12 - recv_cnt;
                        }

                        for (int j = 0; j < n; j++)
                        {
                            readbuf[recv_cnt + j] = buf[j];
                        }
                        recv_cnt += n;
                        if (recv_cnt >= 5)
                        {
                            SengMsg($" Recv Read Data: {bufMsg(readbuf, recv_cnt)}");
                            value = readbuf[0];

                            if (readbuf[0] == 0x11)
                                DATA = (uint)((readbuf[1] << 24) + (readbuf[2] << 16) + (readbuf[3] << 8) + (readbuf[4] << 0));
                            else
                            {
                                switch (byte_sel)
                                {
                                    case 0:
                                        DATA = readbuf[4];
                                        break;
                                    case 1:
                                        DATA = readbuf[3];
                                        break;

                                    case 2:
                                        DATA = readbuf[2];
                                        break;
                                    case 3:
                                        DATA = readbuf[1];
                                        break;
                                    default:
                                        DATA = 0x0;
                                        break;
                                }

                            }
                            break;
                        }

                    }
                    else
                        Thread.Sleep(10);
                }
            }
            else
            {
                SengMsg("串口已关闭");
            }

            if (value != a)
            {
                SengMsg($"fail in send: {ADDR.ToString("X2")}\r\n");
            }
            return value;
        }


        public static byte ReadRegDataARM(byte a, uint ADDR, ref uint DATA)
        {
            byte[] readbuf = new byte[15];
            byte[] buf = new byte[10];
            byte value = 0x00;
            int recv_cnt = 0x00;
            uint byte_sel = ADDR & 0x3;
            //byte_sel = (3 - byte_sel) * 8;

            //TXD0 ADDRESS
            readbuf[0] = a;
            //TXD0 ADDRESS
            readbuf[1] = (byte)((ADDR >> 0) & 0xff);
            readbuf[2] = (byte)((ADDR >> 8) & 0xff);
            readbuf[3] = (byte)((ADDR >> 16) & 0xff);
            readbuf[4] = (byte)((ADDR >> 24) & 0xff);
            if (IsOpen)
            {
                //DiscardInBuffer();
                sPort.Write(readbuf, 0, 5);
                SengMsg($" Send Read Cmd: {bufMsg(readbuf, 5)}");
                int i;
                for (i = 0; i < 100; i++)
                {
                    if (sPort.BytesToRead > 0)
                    {
                        int n = sPort.BytesToRead;//先记录下来，避免某种原因，人为的原因，操作几次之间时间长，缓存不一致     
                        //声明一个临时数组存储当前来的串口数据   

                        if (n >= 8)
                            n = 8;
                        sPort.Read(buf, 0, n);//读取缓冲数据

                        if ((recv_cnt + n) >= 12)
                        {
                            n = 12 - recv_cnt;
                        }

                        for (int j = 0; j < n; j++)
                        {
                            readbuf[recv_cnt + j] = buf[j];
                        }
                        recv_cnt += n;
                        if (recv_cnt >= 5)
                        {
                            SengMsg($" Recv Read Data: {bufMsg(readbuf, recv_cnt)}");
                            value = readbuf[0];

                            if (readbuf[0] == 0x11)
                                DATA = (uint)((readbuf[1] << 0) + (readbuf[2] << 8) + (readbuf[3] << 16) + (readbuf[4] << 24));
                            else
                            {
                                switch (byte_sel)
                                {
                                    case 0:
                                        DATA = readbuf[1];
                                        break;
                                    case 1:
                                        DATA = readbuf[2];
                                        break;

                                    case 2:
                                        DATA = readbuf[3];
                                        break;
                                    case 3:
                                        DATA = readbuf[4];
                                        break;
                                    default:
                                        DATA = 0x0;
                                        break;
                                }

                            }
                            break;
                        }

                    }
                    else
                        Thread.Sleep(10);
                }
            }
            else
            {
                SengMsg("串口已关闭");
            }

            if (value != a)
            {
                SengMsg($"fail in send: {ADDR.ToString("X2")}\r\n");
            }
            return value;
        }


        public static byte SendRegData(byte a, uint ADDR, uint DATA)
        {

            byte[] sendbuf = new byte[9];
            byte[] buf = new byte[9]; //声明一个临时数组存储当前来的串口数据     
            byte value = 0x00;
            int i;
            //TXD0 ADDRESS
            sendbuf[0] = a;//com choose write or byte write
            //TXD0 ADDRESS
            sendbuf[1] = (byte)((ADDR >> 0) & 0xff);
            sendbuf[2] = (byte)((ADDR >> 8) & 0xff);
            sendbuf[3] = (byte)((ADDR >> 16) & 0xff);
            sendbuf[4] = (byte)((ADDR >> 24) & 0xff);
            //大端模式
            sendbuf[5] = (byte)((DATA >> 24) & 0xff);
            sendbuf[6] = (byte)((DATA >> 16) & 0xff);
            sendbuf[7] = (byte)((DATA >> 8) & 0xff);
            sendbuf[8] = (byte)((DATA >> 0) & 0xff);
            if (IsOpen)
            {
                //sPort.DiscardInBuffer();
                sPort.Write(sendbuf, 0, 9);
                SengMsg($" Send Read Cmd: {bufMsg(sendbuf, 9)}");

                for (i = 0; i < 100; i++)
                {
                    if (sPort.BytesToRead > 0)
                    {
                        int n = sPort.BytesToRead;//先记录下来，避免某种原因，人为的原因，操作几次之间时间长，缓存不一致 
                        if (n >= 8)
                            n = 8;
                        sPort.Read(buf, 0, n);//读取缓冲数据
                       
                        SengMsg($" Recv Data: {bufMsg(buf, n)}");
                        value = buf[0];

                        break;
                    }
                    else
                        Thread.Sleep(10);
                }
            }
            else
            {
                SengMsg("串口已关闭");
            }

            if (value != a)
            {
                SengMsg($"fail in send: {ADDR.ToString("X2")}\r\n");
            }
            return value;
        }


        public static byte SendRegDataARM(byte a, uint ADDR, uint DATA)
        {

            byte[] sendbuf = new byte[9];
            byte[] buf = new byte[9]; //声明一个临时数组存储当前来的串口数据     
            byte value = 0x00;
            int i;
            //TXD0 ADDRESS
            sendbuf[0] = a;//com choose write or byte write
            //TXD0 ADDRESS
            sendbuf[1] = (byte)((ADDR >> 0) & 0xff);
            sendbuf[2] = (byte)((ADDR >> 8) & 0xff);
            sendbuf[3] = (byte)((ADDR >> 16) & 0xff);
            sendbuf[4] = (byte)((ADDR >> 24) & 0xff);
            //小端模式
            sendbuf[5] = (byte)((DATA >> 0) & 0xff);
            sendbuf[6] = (byte)((DATA >> 8) & 0xff);
            sendbuf[7] = (byte)((DATA >> 16) & 0xff);
            sendbuf[8] = (byte)((DATA >> 24) & 0xff);
            if (IsOpen)
            {
                //sPort.DiscardInBuffer();
                sPort.Write(sendbuf, 0, 9);
                SengMsg($" Send Read Cmd: {bufMsg(sendbuf, 9)}");

                for (i = 0; i < 100; i++)
                {
                    if (sPort.BytesToRead > 0)
                    {
                        int n = sPort.BytesToRead;//先记录下来，避免某种原因，人为的原因，操作几次之间时间长，缓存不一致 
                        if (n >= 8)
                            n = 8;
                        sPort.Read(buf, 0, n);//读取缓冲数据

                        SengMsg($" Recv Data: {bufMsg(buf, n)}");
                        value = buf[0];

                        break;
                    }
                    else
                        Thread.Sleep(10);
                }
            }
            else
            {
                SengMsg("串口已关闭");
            }

            if (value != a)
            {
                SengMsg($"fail in send: {ADDR.ToString("X2")}\r\n");
            }
            return value;
        }

        static void SengMsg(string msg)
        {
            if (IsShowMsg&&null != SendPort)
            {
                SendPort(msg);
            }
        }
        static string bufMsg(byte[] buf, int len)
        {
            string bMsg = string.Empty;
            for (int i = 0; i < len; i++)
            {
                bMsg += $"{buf[i].ToString("X2")} ";
            }
            return bMsg;
        }

        public static void Isp_Write(uint reg_address, uint reg_data)
        {
            int byte_sel = (int)(reg_address) & 0x3;
            byte_sel = byte_sel * 8;

            reg_data = reg_data & 0xff;
            reg_data = reg_data << byte_sel;
            SendRegData(0x21, reg_address, reg_data);
        }


        public static void AE_Enable() { Isp_Write(0x80140029, 0x1); }

        public static void AE_Disable() { Isp_Write(0x80140029, 0x0); }


        public static void I2C_Switch_Master()
        {
            uint recdata = 0;
            ReadRegData(0x31, 0x80500090, ref recdata);
            recdata |= 0x08;//bit3=1
            SendRegData(0x21, 0x80500090, recdata);
        }

        public static void I2C_Switch_Slaver()
        {
            uint recdata = 0;
            ReadRegData(0x31, 0x80500090, ref recdata);
            recdata &= (uint)(~0x08U);//bit3=0
            SendRegData(0x21, 0x80500090, recdata);
        }
        public static void I2C_Writereg(uint addr, uint val)
        {// I2C 硬件寄存器写
            Isp_Write(0x80250000 + addr, val);
        }
        public static uint I2C_Readreg(uint addr)
        {
            uint I2C_addr_base = 0x80250000;
            return Read(I2C_addr_base + addr);
        }
        public static uint Read(uint reg_address)
        {
            uint val = 0;
            ReadRegData(0x31, reg_address, ref val);
            return val;
        }
        public  static void I2C_RDY()
        {
            uint I2C_addr_base = 0x80250000;
            while ((Read(I2C_addr_base + 0x08) & 0x01) != 0x0) ;
            Thread.Sleep(5);
        }
        public static void I2C_WaitReady()
        {
            UInt16 i;
            uint temp;
            uint status;
            uint I2C_addr_base = 0x80250000;
            if ((I2C_Readreg(0x00) & 0x80) == 0x80)
            {
                status = I2C_Readreg(0x08);
                status = status & (uint)(~0x30U);
                I2C_Writereg(0x08, status);
                for (i = 0; i < 10; i++)
                {
                    temp = (Read(I2C_addr_base + 0x08) >> 4) & 3;
                    if (temp == 3)
                    {
                        break;
                    }
                }
                if (i == 10)
                {
                    status = I2C_Readreg(0x08);
                    status = status | 0x30U;
                    I2C_Writereg(0x08, status);
                }
            }
            else
            {
                I2C_RDY();
            }
        }
        public static void HW_I2C_Write(uint addr, uint val)
        {
            I2C_RDY();
            I2C_Writereg(0x05, addr);
            I2C_Writereg(0x06, val);
            I2C_Writereg(0x04, 0x37);
            Thread.Sleep(20);
            I2C_WaitReady();
        }

        public static void HW_I2C_Write16(uint addr, uint val)
        {
            I2C_RDY();

            I2C_Writereg(0x05, (addr >> 8) & 0xff);
            I2C_Writereg(0x06, addr & 0xff);
            I2C_Writereg(0x04, 0x17);
            Thread.Sleep(20);
            I2C_WaitReady();

            I2C_Writereg(0x06, val);
            I2C_Writereg(0x04, 0x24);
            Thread.Sleep(20);
            I2C_WaitReady();

        }
        public static void HW_I2C_16Write16(uint addr, uint val)
        {
            // I2C_RDY();

            I2C_Writereg(0x05, (addr >> 8) & 0xff);
            I2C_Writereg(0x06, addr & 0xff);
            I2C_Writereg(0x04, 0x17);
            Thread.Sleep(100);
            I2C_WaitReady();
            I2C_Writereg(0x05, (val >> 8) & 0xff);
            I2C_Writereg(0x06, val & 0xff);
            I2C_Writereg(0x04, 0x26);
            Thread.Sleep(100);
            I2C_WaitReady();

        }

        public static uint HW_I2C_Read(uint addr)
        {
            uint val;
            I2C_RDY();
            I2C_Writereg(0x05, addr);
            I2C_Writereg(0x04, 0x33);
            Thread.Sleep(22);
            I2C_WaitReady();
            I2C_Writereg(0x04, 0xf9);
            Thread.Sleep(22);
            I2C_WaitReady();
            val = I2C_Readreg(0x07);
            return val;
        }

        public static uint HW_I2C_Read16(uint addr)
        {
            uint val;
            I2C_RDY();
            I2C_Writereg(0x05, (addr >> 8) & 0xff);
            I2C_Writereg(0x06, addr & 0xff);
            I2C_Writereg(0x04, 0x37);
            Thread.Sleep(0);
            I2C_WaitReady();
            I2C_Writereg(0x04, 0xf9);
            // Thread.Sleep(2);
            I2C_WaitReady();

            val = I2C_Readreg(0x07);
            return val;

        }
        public static uint HW_I2C_16Read16(uint addr)
        {
            uint val, val1, val2;
            //I2C_RDY();
            I2C_Writereg(0x05, (addr >> 8) & 0xff);
            I2C_Writereg(0x06, addr & 0xff);
            I2C_Writereg(0x04, 0x17);
            Thread.Sleep(10);
            I2C_WaitReady();
            I2C_Writereg(0x04, 0x59);
            Thread.Sleep(10);
            I2C_WaitReady();
            val1 = I2C_Readreg(0x07);

            I2C_Writereg(0x04, 0xa8);
            val2 = I2C_Readreg(0x07);
            Thread.Sleep(10);
            I2C_WaitReady();
            val = (val1 << 8) + val2;
            return val;
        }

        public static uint I2C0_Read(uint reg, uint a)
        {
            if (reg == 0xfe)
            {
                I2C_RDY();
                I2C_Writereg(0x05, reg);
                I2C_Writereg(0x06, a);
                I2C_Writereg(0x04, 0x37);
                Thread.Sleep(10);
                I2C_RDY();
                return a;
            }
            else
            {
                uint val;
                I2C_RDY();
                I2C_Writereg(0x05, reg);
                I2C_Writereg(0x04, 0x33);
                Thread.Sleep(10);
                I2C_RDY();
                I2C_Writereg(0x04, 0xf9);
                Thread.Sleep(10);
                I2C_RDY();
                val = Read(0x80250007);
                return val;
            }
        }

        public static uint ISP_Read(uint reg_address)
        {
            uint val = 0;
            ReadRegData(0x31, reg_address, ref val);
            return val;
        }
        public static void ISP_Write(uint reg_address, uint reg_data)
        {
            int byte_sel = (int)(reg_address) & 0x3;
            byte_sel = byte_sel * 8;

            reg_data = reg_data & 0xff;
            reg_data = reg_data << byte_sel;
            SendRegData(0x21, reg_address, reg_data);

        }
        /// <summary>
        /// 写 32位bit 不是I2C
        /// </summary>
        /// <param name="addr">地址</param>
        /// <param name="value">值</param>
        public static void WriteRegister(uint addr, uint value)
        {
            List<byte> m_bRxValue = new List<byte>();
            ulong byte_sel = addr & 0x03;
            byte_sel = byte_sel * 8;
            ulong reg_data = value & 0xff;
            reg_data = ((uint)reg_data << (int)byte_sel);
            m_bRxValue.Add((byte)0x21);
            m_bRxValue.Add((byte)(addr & 0xff));
            m_bRxValue.Add((byte)((addr >> 8) & 0xff));
            m_bRxValue.Add((byte)((addr >> 16) & 0xff));
            m_bRxValue.Add((byte)((addr >> 24) & 0xff));

            m_bRxValue.Add((byte)((reg_data >> 24) & 0xff));
            m_bRxValue.Add((byte)((reg_data >> 16) & 0xff));
            m_bRxValue.Add((byte)((reg_data >> 8) & 0xff));
            m_bRxValue.Add((byte)(reg_data & 0xff));

            //sPort.DiscardInBuffer();
            sPort.Write(m_bRxValue.ToArray(), 0, m_bRxValue.Count);
            SengMsg($"W:0x{addr.ToString("X8")} 0x{value.ToString("X2")}\n");
        }
    }
}
